package com.taotao.search.server.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.taotao.common.core.constant.Constants;
import com.taotao.common.core.utils.StringUtils;
import com.taotao.search.server.domain.SearchResult;
import com.taotao.search.request.SearchParam;
import com.taotao.search.response.ProductSkuResponse;
import com.taotao.search.server.service.ProductService;
import org.apache.lucene.search.join.ScoreMode;
import org.apache.lucene.util.QueryBuilder;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.*;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xcontent.XContentType;
import org.junit.jupiter.params.shadow.com.univocity.parsers.annotations.Parsed;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @className: ProductServiceImpl
 * @author: ZhangYuQi
 * @date: 2024/12/19 下午8:09
 * @Version: 1.0
 * @description:
 */

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public void saveProduct(List<ProductSkuResponse> products) {
        BulkRequest bulkRequest = new BulkRequest();

        products.stream().forEach(product -> {
            IndexRequest indexRequest = new IndexRequest();

            indexRequest.index("product");
            indexRequest.id(product.getSkuId().toString());
            indexRequest.source(JSONObject.toJSONString(product), XContentType.JSON);

            bulkRequest.add(indexRequest);

        });

        try {
            client.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public SearchResult search(SearchParam param) throws IOException {
        //构建SeachSourceBuilder
        SearchSourceBuilder searchSourceBuilder = builderSearchSource(param);
        //构建 SearchRequest
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("product");
        searchRequest.source(searchSourceBuilder);
        //执行查询 获取查询结果
        SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
        //解析查询结果
        SearchResult searchResult = parseSearchResult(search, param);
        System.out.println(JSONObject.toJSONString(searchResult));
        return searchResult;
    }

    private SearchResult parseSearchResult(SearchResponse search,SearchParam param) {

        SearchResult searchResult = new SearchResult();

        SearchHits hits = search.getHits();

        ArrayList<ProductSkuResponse> skuResponseArrayList = new ArrayList<>();
        for (SearchHit hit : hits.getHits()) {
            String sourceAsString = hit.getSourceAsString();
            ProductSkuResponse productSkuResponse = JSON.parseObject(sourceAsString, ProductSkuResponse.class);
            if(StringUtils.isNotEmpty(param.getKeyword())){
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField skuTitle = highlightFields.get("skuTitle");
                productSkuResponse.setSkuTitle(skuTitle.getFragments()[0].toString());
            }
            skuResponseArrayList.add(productSkuResponse);
        }

        searchResult.setProducts(skuResponseArrayList);


        /*** 当前页码*/
        searchResult.setPageNum(param.getPageNum());
        /** 总记录数*/
        searchResult.setTotal(hits.getTotalHits().value);
        /** * 总页码*/
        searchResult.setTotalPages(
                Long.valueOf(searchResult.getTotal()%5==0?searchResult.getTotal()/5:(searchResult.getTotal()/5+1)).intValue()
        );

        //agg聚合操作解析
        Aggregations aggregations = search.getAggregations();
        //品牌
        ParsedLongTerms brandIdAgg = aggregations.get("brandIdAgg");
        List<? extends Terms.Bucket> buckets = brandIdAgg.getBuckets();
        List<SearchResult.BrandVo> brandVos = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            brandVo.setBrandId(Long.valueOf(bucket.getKeyAsString()));

            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedStringTerms brandNameAgg = bucketAggregations.get("brandNameAgg");
            brandVo.setBrandName(brandNameAgg.getBuckets().get(0).getKeyAsString());
            ParsedStringTerms brandImgAgg = bucketAggregations.get("brandImgAgg");
            brandVo.setBrandImg(brandImgAgg.getBuckets().get(0).getKeyAsString());
            brandVos.add(brandVo);
        }
        searchResult.setBrands(brandVos);

        //分类
        ParsedTerms catalogIdAgg = aggregations.get("catalogIdAgg");
        List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
        for (Terms.Bucket bucket : catalogIdAgg.getBuckets()) {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            catalogVo.setCatalogId(Long.valueOf(bucket.getKeyAsString()));
            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedTerms catalogNameAgg = bucketAggregations.get("catalogNameAgg");
            catalogVo.setCatalogName(catalogNameAgg.getBuckets().get(0).getKeyAsString());
            catalogVos.add(catalogVo);
        }
        searchResult.setCatalogs(catalogVos);

        //属性

        ParsedNested attrAgg = aggregations.get("attrAgg");
        ArrayList<SearchResult.AttrVo> attrVos = new ArrayList<>();
        Aggregations attrAggregations = attrAgg.getAggregations();
        ParsedTerms attrIdAgg = attrAggregations.get("attrIdAgg");
        for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            attrVo.setAttrId(Long.valueOf(bucket.getKeyAsString()));
            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedTerms attrNameAgg = bucketAggregations.get("attrNameAgg");
            attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
            ParsedTerms attrValueAgg = bucketAggregations.get("attrValueAgg");
            List<String> values = new ArrayList<>();
            for (Terms.Bucket attrValueAggBucket : attrValueAgg.getBuckets()) {
                values.add(attrValueAggBucket.getKeyAsString());
            }
            attrVo.setAttrValue(values);
            attrVos.add(attrVo);
        }
        searchResult.setAttrs(attrVos);

        return searchResult;
    }

    private SearchSourceBuilder builderSearchSource(SearchParam param) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


        //query
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //must
        if(StringUtils.isNotEmpty(param.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
        }
        //filter
        // brandId
        if(StringUtils.isNotEmpty(param.getBrandId())&& param.getBrandId().size()>0){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",param.getBrandId() ));
        }
        // catalogId
        if(param.getCatalog3Id()!=null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
        }


        // skuPrice 1000_9000  1000_   _9000
        if(StringUtils.isNotBlank(param.getSkuPrice())){
            String[] split = param.getSkuPrice().split("_");
            RangeQueryBuilder skuPrice = QueryBuilders.rangeQuery("skuPrice");
            if(split.length==2){
                skuPrice.lte(split[1]);
                skuPrice.gte(split[0]);
            }else if(param.getSkuPrice().startsWith("_")){
                skuPrice.lte(split[1]);
            }else if(param.getSkuPrice().endsWith("_")){
                skuPrice.gte(split[0]);
            }
            boolQueryBuilder.filter(skuPrice);
        }


        /** attr
         * nested  attrs   1_3:3&2_4:8
         */
        if(param.getAttrs()!=null && param.getAttrs().size()>0){

            for (String attrStr : param.getAttrs()) {

                String[] s = attrStr.split("_");
                Long attrId = Long.valueOf(s[0]);

                String[] sttrValues = s[1].split(":");

                BoolQueryBuilder boolNestedQueryBuilder = QueryBuilders.boolQuery();
                boolNestedQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                boolNestedQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue",sttrValues));

                boolQueryBuilder.filter(QueryBuilders.nestedQuery("attrs", boolNestedQueryBuilder, ScoreMode.None));
            }
        }
        searchSourceBuilder.query(boolQueryBuilder);



        //排序 sort  saleCount_desc
        if(StringUtils.isNotBlank(param.getSort())){
            String[] split = param.getSort().split("_");
            searchSourceBuilder.sort(split[0], "asc".equalsIgnoreCase(split[1])?SortOrder.ASC:SortOrder.DESC);
        }

        // 分页
        if(param.getPageNum()!=null){
            searchSourceBuilder.from((param.getPageNum()-1)*5);
            searchSourceBuilder.size(5);
        }

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("skuTitle");
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);




        //brandAgg
        TermsAggregationBuilder brandIdAgg = AggregationBuilders.terms("brandIdAgg").field("brandId");

        TermsAggregationBuilder brandNameAgg = AggregationBuilders.terms("brandNameAgg").field("brandName");

        TermsAggregationBuilder brandImgAgg = AggregationBuilders.terms("brandImgAgg").field("brandImg");

        brandIdAgg.subAggregation(brandNameAgg);
        brandIdAgg.subAggregation(brandImgAgg);

        // catalogAgg
        TermsAggregationBuilder catalogIdAgg = AggregationBuilders.terms("catalogIdAgg").field("catalogId");

        TermsAggregationBuilder catalogNameAgg = AggregationBuilders.terms("catalogNameAgg").field("catalogName");

        catalogIdAgg.subAggregation(catalogNameAgg);

        NestedAggregationBuilder nested = AggregationBuilders.nested("attrAgg", "attrs");

        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId");

        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg").field("attrs.attrName");

        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue");

        nested.subAggregation(attrIdAgg);
        attrIdAgg.subAggregation(attrNameAgg);
        attrIdAgg.subAggregation(attrValueAgg);

        searchSourceBuilder.aggregation(brandIdAgg);
        searchSourceBuilder.aggregation(catalogIdAgg);
        searchSourceBuilder.aggregation(nested);


        return searchSourceBuilder;


    }
}
